import {h} from 'vue'
import {NIcon} from 'naive-ui'
import screenfull from 'screenfull'
import throttle from 'lodash/throttle'
import Image_404 from '../assets/images/exception/image-404.png'
import html2canvas from 'html2canvas'
import {downloadByA} from './file'
import {toString} from './type'
import cloneDeep from 'lodash/cloneDeep'
import {WinKeyboard} from '@/enums/editPageEnum'
import {RequestHttpIntervalEnum, RequestParamsObjType} from '@/enums/httpEnum'
import {CreateComponentType, CreateComponentGroupType} from '@/packages/index.d'
// @ts-ignore
import Utils from "@/vue2-flow/src/utils/Common.js";
//挂载在 window 方便与在js中使用
import _ from 'lodash';
import {MessageOptions} from "naive-ui/es/message/src/types";

export const MessageError = (Text: string, Options: MessageOptions = {}) => {
    window['$message'].error(Text, Options)
}

export const MessageSuccess = (Text: string) => {
    window['$message'].success(Text)
}
export const MessageLoading = (Text: string) => {
    window['$message'].loading(Text)
}
export const MessageWarning = (Text: string) => {
    window['$message'].warning(Text)
}

interface ResType {
    update: boolean;
    val: any;
    param: string;
    has: boolean;
    nodeParam: string;
    nodeId: string;
}

/**
 * 判断属性是否需要被更新
 *
 * @param chartConfig
 * @param Node
 * @constructor
 */
export const HasNodeOutParam = (chartConfig: CreateComponentType, Node: any) => {
    const _List = [] as any
    if (Utils.IsEmpty(chartConfig.option.datasource) || typeof chartConfig.option.datasource === "string") {
        return _List
    }
    const datasource = chartConfig.option.datasource as any
    const hasNode = datasource.nodeIds?.includes(Node.id)
    if (hasNode) {
        //const ConfigParams = Object.keys(chartConfig.option) // 组件内所有的可配置属性
        const ConfigParams2 = GetSecondLevelPropertyPaths(chartConfig.option) // 组件内所有的可配置属性
        for (const OutParam of Node.NodeOutParam.OutParams) {
            const NodeKey = [Node.id, OutParam.ParamName].join('_')
            const ConfigParam = datasource.params[NodeKey]
            if (ConfigParams2.includes(ConfigParam)) {
                _List.push({
                    update: true,
                    val: OutParam.Value,
                    nodeId: Node.id,
                    nodeParam: OutParam.ParamName,
                    param: ConfigParam,
                    chartConfigId: chartConfig.id
                })
            }
        }
    }
    return _List
}

function GetSecondLevelPropertyPaths(obj: object) {
    const paths = [];
    for (const [firstLevelKey, firstLevelValue] of Object.entries(obj)) {
        if (typeof firstLevelValue === 'object' && firstLevelValue !== null) {
            for (const [secondLevelKey] of Object.entries(firstLevelValue)) {
                paths.push(`${firstLevelKey}.${secondLevelKey}`);
            }
        } else {
            paths.push(`${firstLevelKey}`);
        }
    }
    return paths;
}

/**
 * @deprecated 这个方法已经弃用， 请使用 HasNodeOutParam 替代
 * @param chartConfig
 * @param Node
 * @param Param
 * @constructor
 */
export const HasNodeOutParamMVVM = (chartConfig: CreateComponentType, Node: any, Param: string) => {
    console.warn('这个方法已经弃用， 请使用 HasNodeOutParam 替代')
    const _Res: ResType = {
        update: false, val: null,
        param: '', has: false, nodeParam: '', nodeId: ''
    }
    if (Utils.IsEmpty(chartConfig.option.datasource)) {
        return _Res
    }
    const datasource = chartConfig.option.datasource as any
    const hasNode = datasource.nodeIds.includes(Node.id)
    _Res.has = hasNode
    if (hasNode) {
        const onParamKeys = Object.keys(datasource.params)
        for (const OutParam of Node.NodeOutParam.OutParams) {
            const Key = [Node.id, OutParam.ParamName].join('_')
            if (!Utils.IsEmpty(Param)) {
                // Param = Key
            }
            if (datasource.params[Key] === Param) {
                _Res.update = true
                _Res.val = OutParam.Value
                _Res.nodeId = Node.id
                _Res.nodeParam = OutParam.ParamName
                _Res.param = datasource.params[Key]
                break
            }
            if (onParamKeys.includes(Param)) {
                // _Res.update = true
                // _Res.val = OutParam.Value
                // _Res.nodeId = Node.id
                // _Res.nodeParam = OutParam.ParamName
                // _Res.param = datasource.params[Key]
                // break
            }
        }
    }
    if (typeof Node === 'string') {
        const Param = Node.toString()
        let gp = []
        for (let paramsKey in datasource.params) {
            const _Val = datasource.params[paramsKey]
            if (_Val === Param) {
                gp = paramsKey.split('_')
                _Res.update = true
                _Res.has = true
                _Res.nodeId = gp[0]
                _Res.nodeParam = gp.slice(1).join('_')
                _Res.param = Param
                break
            }
        }
    }
    return _Res
}

export const HasGlobalOutParam = (chartConfig: CreateComponentType, GlobalParam: any) => {
    const _List = [] as any
    if (Utils.IsEmpty(chartConfig.option.datasource) || typeof chartConfig.option.datasource === "string") {
        return _List
    }
    const datasource = chartConfig.option.datasource as any
    const hasNode = datasource.nodeIds.includes(GlobalParam.Id)
    if (hasNode) {
        // const ConfigParams = Object.keys(chartConfig.option) // 组件内所有的可配置属性
        const ConfigParams2 = GetSecondLevelPropertyPaths(chartConfig.option)
        const GlobalParamKey = [GlobalParam.Id, GlobalParam.Name].join('_')
        const ConfigParam = datasource.params[GlobalParamKey]
        if (ConfigParams2.includes(ConfigParam)) {
            _List.push({
                update: true,
                val: GlobalParam.Value,
                nodeId: GlobalParam.id,
                nodeParam: GlobalParam.ParamName,
                param: ConfigParam
            })
        }
    }
    return _List
}

/**
 * * 判断是否是开发环境
 * @return { Boolean }
 */
export const isDev = () => {
    return import.meta.env.DEV
}

/**
 * * 生成一个不重复的ID
 * @param { Number } randomLength
 */
export const getUUID = (randomLength = 10) => {
    return 'id_' + Number(Math.random().toString().substring(2, randomLength) + Date.now()).toString(36)
}

/**
 * * render 图标
 *  @param icon 图标
 *  @param set 设置项
 */
export const renderIcon = (icon: any, set = {}) => {
    return () => h(NIcon, set, {default: () => h(icon)})
}
/**
 * * render 语言
 *  @param lang 语言标识
 *  @param set 设置项
 *  @param tag 要渲染成的标签
 */
export const renderLang = (lang: string, set = {}, tag = 'span') => {
    return () => h(tag, set, {default: () => window['$t'](lang)})
}

/**
 * * 获取错误处理图片，默认 404 图
 * @returns url
 */
export const requireErrorImg = () => {
    return Image_404
}

/**
 * * 全屏操作函数
 * @param isFullscreen
 * @param isEnabled
 * @returns
 */
export const screenfullFn = (isFullscreen?: boolean, isEnabled?: boolean) => {
    // 是否是全屏
    if (isFullscreen) return screenfull.isFullscreen

    // 是否支持全屏
    if (isEnabled) return screenfull.isEnabled

    if (screenfull.isEnabled) {
        screenfull.toggle()
        return
    }
    // TODO lang
    window['$message'].warning('您的浏览器不支持全屏功能！')
}

/**
 * 修改元素位置
 * @param target 对象
 * @param x X轴
 * @param y Y轴
 */
export const setComponentPosition = (
    target: CreateComponentType | CreateComponentGroupType,
    x?: number,
    y?: number
) => {
    x && (target.attr.x = x)
    y && (target.attr.y = y)
}

/**
 * * 设置元素属性
 * @param HTMLElement 元素
 * @param key 键名
 * @param value 键值
 */
export const setDomAttribute = <K extends keyof CSSStyleDeclaration, V extends CSSStyleDeclaration[K]>(
    HTMLElement: HTMLElement,
    key: K,
    value: V
) => {
    if (HTMLElement) {
        HTMLElement.style[key] = value
    }
}

/**
 * * 判断是否是 mac
 * @returns boolean
 */
export const isMac = () => {
    return /macintosh|mac os x/i.test(navigator.userAgent)
}

/**
 * * file转url
 */
export const fileToUrl = (file: File): string => {
    const Url = URL || window.URL || window.webkitURL
    const ImageUrl = Url.createObjectURL(file)
    return ImageUrl
}

/**
 * * file转base64
 */
export const fileTobase64 = (file: File, callback: Function) => {
    let reader = new FileReader()
    reader.readAsDataURL(file)
    reader.onload = function (e: ProgressEvent<FileReader>) {
        if (e.target) {
            let base64 = e.target.result
            callback(base64)
        }
    }
}

/**
 * * 挂载监听
 */
// eslint-disable-next-line no-undef
export const addEventListener = <K extends keyof WindowEventMap>(
    target: HTMLElement | Document,
    type: K,
    listener: any,
    delay?: number,
    // eslint-disable-next-line no-undef
    options?: boolean | AddEventListenerOptions | undefined
) => {
    if (!target) return
    target.addEventListener(
        type,
        throttle(listener, delay || 300, {
            leading: true,
            trailing: false
        }),
        options
    )
}

/**
 * * 卸载监听
 */
// eslint-disable-next-line no-undef
export const removeEventListener = <K extends keyof WindowEventMap>(
    target: HTMLElement | Document,
    type: K,
    listener: any
) => {
    if (!target) return
    target.removeEventListener(type, listener)
}

export const exportHandle = () => {

    // 导出图片
    const range = document.body as HTMLElement
    const watermark = document.getElementById('go-edit-watermark')
    // 隐藏边距线
    if (!range) {
        window['$message'].error('导出失败！')
        return
    }
    // 记录缩放比例
    //@ts-ignore 展示水印
    watermark.style.display = 'block'

    setTimeout(() => {
        canvasCut(range, () => {
            // 隐藏水印
            if (watermark) watermark.style.display = 'none'
            // 还原页面大小
        })
    }, 1500)
}
/**
 * * 截取画面为图片并下载
 * @param html 需要截取的 DOM
 */
export const canvasCut = (html: HTMLElement | null, callback?: Function) => {
    if (!html) {
        window['$message'].error('导出失败！')
        if (callback) callback()
        return
    }

    html2canvas(html, {
        backgroundColor: null,
        allowTaint: true,
        useCORS: true
    }).then((canvas: HTMLCanvasElement) => {
        window['$message'].success('导出成功！')
        downloadByA(canvas.toDataURL(), undefined, 'png')
        if (callback) callback()
    })
}

/**
 * * 函数过滤器
 * @param data 数据值
 * @param res 返回顶级对象
 * @param funcStr 函数字符串
 * @param isToString 是否转为字符串
 * @param errorCallBack 错误回调函数
 * @param successCallBack 成功回调函数
 * @returns
 */
export const newFunctionHandle = (
    data: any,
    res: any,
    funcStr?: string,
    isToString?: boolean,
    errorCallBack?: Function,
    successCallBack?: Function
) => {
    try {
        if (!funcStr) return data
        const fn = new Function('data', 'res', funcStr)
        const fnRes = fn(cloneDeep(data), cloneDeep(res))
        const resHandle = isToString ? toString(fnRes) : fnRes
        // 成功回调
        successCallBack && successCallBack(resHandle)
        return resHandle
    } catch (error) {
        // 失败回调
        errorCallBack && errorCallBack(error)
        return '函数执行错误'
    }
}

/**
 * * 处理请求事件单位
 * @param num 时间间隔
 * @param unit RequestHttpIntervalEnum
 * @return number 秒数
 */
export const intervalUnitHandle = (num: number, unit: RequestHttpIntervalEnum) => {
    switch (unit) {
        // 秒
        case RequestHttpIntervalEnum.SECOND:
            return num * 1000
        // 分
        case RequestHttpIntervalEnum.MINUTE:
            return num * 1000 * 60
        // 时
        case RequestHttpIntervalEnum.HOUR:
            return num * 1000 * 60 * 60
        // 天
        case RequestHttpIntervalEnum.DAY:
            return num * 1000 * 60 * 60 * 24
        default:
            return num * 1000
    }
}

/**
 * * 对象转换 cookie 格式
 * @param obj
 * @returns string
 */
export const objToCookie = (obj: RequestParamsObjType) => {
    if (!obj) return ''

    let str = ''
    for (const key in obj) {
        str += key + '=' + obj[key] + ';'
    }
    return str.substring(0, str.length - 1)
}

/**
 * * 设置按下键盘按键的底部展示
 * @param keyCode
 * @returns
 */
export const setKeyboardDressShow = (keyCode?: number) => {
    const code = new Map([
        [17, WinKeyboard.CTRL],
        [32, WinKeyboard.SPACE]
    ])

    const dom = document.getElementById('keyboard-dress-show')
    if (!dom) return
    if (!keyCode) {
        window.onKeySpacePressHold?.(false)
        dom.innerText = ''
        return
    }
    if (keyCode && code.has(keyCode)) {
        if (keyCode == 32) window.onKeySpacePressHold?.(true)
        dom.innerText = `按下了「${code.get(keyCode)}」键`
    }
}

/**
 * * JSON序列化，支持函数和 undefined
 * @param data
 */
export const JSONStringify = <T>(data: T, space = 2) => {
    return JSON.stringify(
        data,
        (key, val) => {
            // 处理函数丢失问题
            if (typeof val === 'function') {
                return `${val}`
            }
            // 处理 undefined 丢失问题
            if (typeof val === 'undefined') {
                return null
            }
            return val
        },
        space
    )
}

export const evalFn = (fn: string) => {
    var Fun = Function // 一个变量指向Function，防止前端编译工具报错
    return new Fun('return ' + fn)()
}

/**
 * * JSON反序列化，支持函数和 undefined
 * @param data
 */
export const JSONParse = (data: string) => {
    if (Utils.IsEmpty(data) || data.trim() === '') return
    return JSON.parse(data, (k, v) => {
        // // 过滤函数字符串
        // if (excludeParseEventKeyList.includes(k)) return v
        // // 过滤函数值表达式
        // if (typeof v === 'string') {
        //   const someValue = excludeParseEventValueList.some(excludeValue => v.indexOf(excludeValue) > -1)
        //   if (someValue) return v
        // }
        if (k !== 'formatter') {
            return v
        }
        // 还原函数值
        if (typeof v === 'string' && v.indexOf && (v.indexOf('function') > -1 || v.indexOf('=>') > -1)) {
            return evalFn(`(function(){return ${v}})()`)
        } else if (typeof v === 'string' && v.indexOf && v.indexOf('return ') > -1) {
            const baseLeftIndex = v.indexOf('(')
            if (baseLeftIndex > -1) {
                const newFn = `function ${v.substring(baseLeftIndex)}`
                return evalFn(`(function(){return ${newFn}})()`)
            }
        }
        return v
    })
}

/**
 * * 修改顶部标题
 * @param title
 */
export const setTitle = (title?: string) => {
    title && (document.title = title)
}

/**
 * 键盘按下事件
 * 回调成功之后会自动注销事件
 * @param callback 回调函数
 * @param delay 防呆时间
 * @param ignores 忽略监听的键
 * @param onKeyDownNumber 监听键盘的次数，0表示移植监听
 */
export const addEventListenerKeydown = (callback: Function, delay: number = 100, ignores: string[] = [], onKeyDownNumber: number = 1) => {
    console.log('ignores=', ignores)
    let keyBuffer = ''
    let MaxKeydownNumber = 1
    let timer: number | null | undefined = null;
    const KeyHandel = (e: KeyboardEvent) => {
        const _Key = e.key as string;
        if (!Utils.IsEmpty(ignores) && ignores.map(iitem => iitem.toLowerCase()).includes(_Key.toLowerCase())) {
            return
        }
        keyBuffer += _Key
        if (timer) {
            clearTimeout(timer);
        }
        // @ts-ignore
        timer = setTimeout(() => {
            callback(keyBuffer);
            keyBuffer = '';
            ++MaxKeydownNumber
            if (onKeyDownNumber != 0 && MaxKeydownNumber > onKeyDownNumber) {
                document.removeEventListener('keydown', KeyHandel)
            }
        }, delay);
    }
    document.addEventListener('keydown', KeyHandel)
}
